package com.zero.ddd.akka.event.publisher2.backoff;

import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

import org.springframework.util.backoff.BackOff;
import org.springframework.util.backoff.BackOffExecution;
import org.springframework.util.backoff.ExponentialBackOff;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * @say little Boy, don't be sad.
 * @name Rezar
 * @time 2024-01-12 11:16:10
 * @Desc 些年若许,不负芳华.
 *
 */
@Slf4j
public class ExponentialBackoffRetry {
	
	private BackOff exponentialBackOff;

	public ExponentialBackoffRetry() {
		this.exponentialBackOff = this.exponentialBackOff();
	}
	
	protected <T> T operationWithBackoffRetryAndThrowError(
			String operationDesc,
			Supplier<T> optSupplier) {
		return 
				this.operationWithBackoffRetry(
						operationDesc, 
						optSupplier)
				.orElseThrow(() -> {
					return new IllegalStateException(
							"重试operation:[" + operationDesc + "] 失败, 超过重试次数");
				});
	}
	
	protected <T> T operationWithBackoffRetryAndThrowError(
			String operationDesc,
			Supplier<T> optSupplier,
			BackOff exponentialBackOff) {
		return 
				this.operationWithBackoffRetry(
						operationDesc, 
						optSupplier,
						exponentialBackOff)
				.orElseThrow(() -> {
					return new IllegalStateException(
							"重试operation:[" + operationDesc + "] 失败, 超过重试次数");
				});
	}
	
	protected <T> Optional<T> operationWithBackoffRetry(
			String operationDesc,
			Supplier<T> optSupplier) {
		return 
				this.operationWithBackoffRetry(
						operationDesc,
						optSupplier, 
						this.exponentialBackOff);
	}
	
	protected <T> Optional<T> operationWithBackoffRetry(
			String operationDesc,
			Supplier<T> optSupplier,
			BackOff exponentialBackOff) {
		BackOffExecution execution = null;
		do {
			try {
				return 
						Optional.ofNullable(
								optSupplier.get());
			} catch (Exception e) {
				if (execution == null) {
					execution = exponentialBackOff.start();
				}
				long nextBackOff = execution.nextBackOff();
				log.error(
						"operation:[" + operationDesc + "] error while operationWithBackoffRetry:{}, will retry with backoff:" + nextBackOff,
						e);
				if (nextBackOff == BackOffExecution.STOP) {
					break;
				}
				try {
					TimeUnit.MILLISECONDS.sleep(nextBackOff);
				} catch (InterruptedException e1) {
					break;
				}
			}
		} while(true);
		log.error("重试operation:[" + operationDesc + "] 失败, 超过重试次数");
		return Optional.empty();
	}
	
	protected BackOff exponentialBackOff() {
		long initialInterval = 200;			// 初始间隔
		long maxInterval = 10 * 1000L;		// 最大间隔
		long maxElapsedTime = 600 * 1000L;	// 最大时间间隔
		double multiplier = 1.1;			// 递增倍数（即下次间隔是上次的多少倍）
		ExponentialBackOff backOff = 
				new ExponentialBackOff(
						initialInterval,
						multiplier);
		backOff.setMaxInterval(maxInterval);
		backOff.setMaxElapsedTime(maxElapsedTime);
		return backOff;
	}

}